package com.studynotes.dev.tech.common.utils;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.http.Header;
import cn.hutool.http.HttpStatus;
import cn.hutool.json.JSONUtil;
import com.google.common.collect.Multimap;
import com.studynotes.dev.tech.common.api.ApiResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.HandlerMapping;

import javax.servlet.ServletRequest;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * @author Liu XiaoJun
 * @description: ServletUtil
 * @date 2025-11-07 13:56:26
 */
@Slf4j
public class ServletUtil {

    private ServletUtil() {
        throw new IllegalStateException("Utility class");
    }

    /**
     * getRequest
     *
     * @return HttpServletRequest
     */
    @SuppressWarnings("unused")
    public static HttpServletRequest getRequest()
    {
        ServletRequestAttributes attributes = getRequestAttributes();
        if (null == attributes) {
            return null;
        }
        return attributes.getRequest();
    }

    /**
     * getResponse
     *
     * @return HttpServletResponse
     */
    @SuppressWarnings("unused")
    public static HttpServletResponse getResponse()
    {
        ServletRequestAttributes attributes = getRequestAttributes();
        if (null == attributes) {
            return null;
        }
        return attributes.getResponse();
    }

    /**
     * getSession
     *
     * @return HttpSession
     */
    @SuppressWarnings("unused")
    public static HttpSession getSession()
    {
        HttpServletRequest request = getRequest();
        if (null == request) {
            return null;
        }
        return request.getSession();
    }

    /**
     * getCookies
     *
     * @return Cookie[]
     */
    @SuppressWarnings("unused")
    public static Cookie[] getCookies() {
        HttpServletRequest request = getRequest();
        if (null == request) {
            return new Cookie[0];
        }
        return request.getCookies();
    }

    /**
     * getRequestAttributes
     *
     * @return ServletRequestAttributes
     */
    public static ServletRequestAttributes getRequestAttributes()
    {
        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
        Assert.notNull(attributes);
        return (ServletRequestAttributes) attributes;
    }

    /**
     * 判断是否是外部请求
     *
     * @return boolean
     */
    public static boolean isRequest() {
        return Optional.ofNullable(RequestContextHolder.getRequestAttributes()).isPresent();
    }

    /**
     * getClientIp
     *
     * @return clientIp
     */
    @SuppressWarnings("unused")
    public static String getClientIp() {
        return cn.hutool.extra.servlet.ServletUtil.getClientIP(getRequest());
    }

    /**
     * getBaseUrl
     *
     * @return baseUrl
     */
    @SuppressWarnings("unused")
    public static String getBaseUrl() {
        HttpServletRequest request = getRequest();
        if (null == request) {
            return null;
        }
        return request.getScheme() + "://"+ request.getServerName() +
                (request.getServerPort() != 80 && request.getServerPort() != 443 ? ":"+ request.getServerPort() : "") +
                request.getContextPath() +"/";
    }

    /**
     * 获取user agent
     *
     * @return userAgent
     */
    public static String getUserAgent() {
        HttpServletRequest request = getRequest();
        if (null == request) {
            return null;
        }
        return cn.hutool.extra.servlet.ServletUtil.getHeader(request, Header.USER_AGENT.getValue(), StandardCharsets.UTF_8);
    }

    /**
     * 判断url是否在规则中
     *
     * @param rules set of rule
     * @return Boolean
     */
    public static Boolean urlInRule(Set<String> rules) {
        HttpServletRequest request = getRequest();
        if (null == request) {
            return false;
        }
        String url = request.getRequestURI();
        PathMatcher matcher = new AntPathMatcher();
        Optional<String> optional = rules.stream().filter(item -> matcher.match(item, url)).findFirst();
        return optional.isPresent();
    }

    /**
     * 判断yrl是否在规则中
     * <p>
     *     规则map中key为url规则，value为可用的请求方式，例如：GET，POST等
     * </p>
     *
     * @param rules 规则
     * @return boolean
     */
    public static boolean urlInRule(Multimap<HttpMethod, String> rules) {
        HttpServletRequest request = getRequest();
        if (null == request) {
            return false;
        }
        String method = request.getMethod();
        HttpMethod httpMethod = HttpMethod.resolve(method.toUpperCase());
        return  urlInRule(new HashSet<>(rules.get(httpMethod)));
    }

    /**
     * 验证参数是否json格式
     *
     * @param request 请求
     * @return Boolean
     */
    public static boolean isJsonRequest(ServletRequest request) {
        return CharSequenceUtil.indexOfIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE) > -1;
    }

    /**
     * 验证参数是否FormData格式
     *
     * @param request 请求
     * @return Boolean
     */
    public static boolean isFormDataRequest(ServletRequest request) {
        return CharSequenceUtil.indexOfIgnoreCase(request.getContentType(), MediaType.MULTIPART_FORM_DATA_VALUE) > -1;
    }

    /**
     * 验证参数是否FormUrl格式
     *
     * @param request 请求
     * @return Boolean
     */
    public static boolean isFormUrlRequest(ServletRequest request) {
        return CharSequenceUtil.indexOfIgnoreCase(request.getContentType(), MediaType.APPLICATION_FORM_URLENCODED_VALUE) > -1;
    }

    /**
     * 返回附件
     *
     * @param response 响应
     * @param filename 文件名
     * @param content 附件内容
     * @throws IOException exception
     */
    public static void writeAttachment(HttpServletResponse response, String filename, byte[] content) throws IOException {
        response.setStatus(HttpStatus.HTTP_OK);
        // 设置 header 和 contentType
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
        // 输出附件
        IoUtil.write(response.getOutputStream(), false, content);
    }

    /**
     * 返回api结果
     *
     * @param response 响应
     * @param code 状态码
     * @param apiResult api结果
     */
    public static void writeApiResult(HttpServletResponse response, Integer code, ApiResult<?>apiResult) {
        response.setStatus(code);
        cn.hutool.extra.servlet.ServletUtil.write(response, JSONUtil.toJsonStr(apiResult), MediaType.APPLICATION_JSON_UTF8_VALUE);
    }

    /**
     * 返回api结果
     *
     * @param response 响应
     * @param apiResult api结果
     */
    public static void writeApiResult(HttpServletResponse response, ApiResult<?> apiResult) {
        response.setStatus(HttpStatus.HTTP_OK);
        cn.hutool.extra.servlet.ServletUtil.write(response, JSONUtil.toJsonStr(apiResult), MediaType.APPLICATION_JSON_UTF8_VALUE);
    }

    /**
     * 获取匹配的url
     *
     * @param request 请求
     * @return pattern
     */
    public static String buildPattern(HttpServletRequest request) {
        return (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
    }

    /**
     * 获取session
     *
     * @param request 请求
     * @return sessions
     */
    public static Map<String, Object> buildSessions(HttpServletRequest request) {
        Enumeration<String> keys = request.getSession().getAttributeNames();
        Map<String, Object> sessions = new HashMap<>();
        while (keys.hasMoreElements()) {
            String key = keys.nextElement();
            sessions.put(key, request.getSession().getAttribute(key));
        }
        return sessions;
    }

    /**
     * 获取请求头
     *
     * @param request 请求
     * @return headers
     */
    public static Map<String, String> buildHeaders(HttpServletRequest request) {
        Enumeration<String> keys = request.getHeaderNames();
        Map<String, String> headers = new HashMap<>();
        while (keys.hasMoreElements()) {
            String key = keys.nextElement();
            headers.put(key, request.getHeader(key));
        }
        return headers;
    }

    /**
     * 获取请求体
     *
     * @param request 请求
     * @return body
     */
    public static Map<String, Object> buildBody(HttpServletRequest request) {
        Map<String, Object> body = new HashMap<>();
        if (CollectionUtil.containsAny(request.getMethod().toUpperCase(), "POST", "PUT", "PATCH")) {
            if (isJsonRequest(request)) {
                try {
                    Object bodyObject = JsonUtil.parseInputStream(request.getInputStream(), Object.class);
                    if (bodyObject instanceof Map) {
                        body.putAll((Map<String, ?>) bodyObject);
                    } else {
                        body.put("request", bodyObject);
                    }
                } catch (IOException e) {
                    log.warn("request body parse error: {}", e.getMessage());
                }
            } else if (isFormDataRequest(request)) {
                MultipartHttpServletRequest multipartHttpServletRequest = (MultipartHttpServletRequest) request;
                body.putAll(multipartHttpServletRequest.getFileMap());
            } else if (isFormUrlRequest(request)) {
                Map<String, List<Object>> parameterMap = new HashMap<>(request.getParameterMap().size());
                request.getParameterMap().forEach((key, values) -> {
                    parameterMap.put(key, Arrays.asList(values));
                });
                body.putAll(parameterMap);
            }
        }
        return body;
    }
}
